// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Data
{
    internal class SqlBuildClause : IList<SqlElement>
    {
        protected List<SqlElement> _elements;
        protected Action<ISqlParameter> _changed;

        public SqlBuildClause(Action<ISqlParameter> changed)
        {
            _changed = changed;
            _elements = new List<SqlElement>();
        }

        public void AddParameter(ISqlParameter parameter)
        {
            this.Changed(parameter);
        }

        public void AddParameters(IEnumerable<ISqlParameter> parameters)
        {
            foreach (var parameter in parameters)
            {
                this.Changed(parameter);
            }
        }

        public SqlElement this[int index]
        {
            get => _elements[index];
            set
            {
                _elements[index] = value;

                this.Changed();
            }
        }

        public int Count => _elements.Count;

        public bool IsReadOnly => false;

        public void Add(SqlElement item)
        {
            _elements.Add(item);

            this.Changed();
        }

        public void AddRange(IEnumerable<SqlElement> items)
        {
            _elements.AddRange(items);

            this.Changed();
        }

        public void Clear()
        {
            _elements.Clear();

            this.Changed();
        }

        public bool Contains(SqlElement item)
        {
            return _elements.Contains(item);
        }

        public bool Has<TElement>()
        {
            return _elements.Exists(m => m is TElement);
        }

        public void CopyTo(SqlElement[] array, int arrayIndex)
        {
            _elements.CopyTo(array, arrayIndex);
        }

        public int IndexOf(SqlElement item)
        {
            return _elements.IndexOf(item);
        }

        public void Insert(int index, SqlElement item)
        {
            _elements.Insert(index, item);

            this.Changed();
        }

        public void InsertRange(int index, IEnumerable<SqlElement> items)
        {
            _elements.InsertRange(index, items);

            this.Changed();
        }

        public bool Remove(SqlElement item)
        {
            if (_elements.Remove(item))
            {
                this.Changed();

                return true;
            }

            return false;
        }

        public void RemoveAt(int index)
        {
            _elements.RemoveAt(index);

            this.Changed();
        }

        public int RemoveAll(Predicate<SqlElement> predicate)
        {
            this.Changed();
            return _elements.RemoveAll(predicate);
        }

        public bool TryGet<TElement>(out TElement element) where TElement : SqlElement
        {
            var first = _elements.FirstOrDefault(m => m is TElement);

            element = first != null ? (TElement)first : null;

            return first != null;
        }

        public bool TryGet<TElement>(Predicate<TElement> predicate, out TElement element) where TElement : SqlElement
        {
            var first = _elements.FirstOrDefault(m => m is TElement item && predicate(item));

            element = first != null ? (TElement)first : null;

            return first != null;
        }

        public IEnumerator<SqlElement> GetEnumerator()
        {
            return _elements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public SqlBuildClause Clone(Action<ISqlParameter> changed)
        {
            return new SqlBuildClause(changed)
            {
                _elements = _elements.Select(m => m.Clone()).ToList()
            };
        }

        public void Changed()
        {
            this.Changed(null);
        }

        private void Changed(ISqlParameter sqlParameter)
        {
            _changed(sqlParameter);
        }
    }
}
